/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "ScriptComponent.h"
#include "TransformComponent.h"

#include "SceneObject.h"
#include "GameObjectSerializer.h"
#include "RigidBodyComponent.h"

namespace PQEngine
{
	GameObjectSerializer::GameObjectSerializer()
	{
	}

	GameObjectSerializer& GameObjectSerializer::getSingleton()
	{
		static GameObjectSerializer inst;
		return inst;
	}

	SerializeDatagram* GameObjectSerializer::serialize()
	{
		_generator=new SerializeGenerator(SerializeDataId_GameObjectTree);
		_root=SceneObject::getActive()->getRoot();

		serializeGameObject(_root);
		_datagram= _generator->genDatagram();
		delete _generator;
		_generator=0;
		return _datagram;
	}

	void GameObjectSerializer::serializeGameObject(GameObject* object)
	{
		_generator->addIntParam(OBJECT_TYPE_GAME_OBJECT);
		_generator->addStringParam(object->getName());
		int count=object->getComponentCount();
		_generator->addIntParam(count);//components count
		for(int i=0;i<count;i++){
			Component* component= object->getComponent(i);
			ObjectType type=component->getObjectType();
			switch(type){
				case OBJECT_TYPE_COMPONENT_TRANSFORM:
					serializeTransformComponent((TransformComponent*)component);
					break;
				case OBJECT_TYPE_COMPONENT_MESH:
					serializeEntityComponent((MeshComponent*)component);
					break;
				case OBJECT_TYPE_COMPONENT_SCRIPT:
					serializeScriptComponent((ScriptComponent*)component);
					break;
				case OBJECT_TYPE_COMPONENT_RIGIDBODY:
					serializeRigidBodyComponent((RigidBodyComponent*)component);
					break;
				case OBJECT_TYPE_COMPONENT_PLANE_COLLIDER:
					serializePlaneColliderComponent((PlaneColliderComponent*)component);
					break;
				case OBJECT_TYPE_COMPONENT_BOX_COLLIDER:
					serializeBoxColliderComponent((BoxColliderComponent*)component);
					break;
				case OBJECT_TYPE_COMPONENT_SPHERE_COLLIDER:
					serializeSphereColliderComponent((SphereColliderComponent*)component);
					break;
			}
		}

		count=object->getChildrenCount();////child count
		_generator->addIntParam(count);
		for(int i=0;i<count;i++){
			GameObject* child= object->getChild(i);
			serializeGameObject(child);
		}
	}

	void GameObjectSerializer::serializeScriptComponent(ScriptComponent* component)
	{
		_generator->addIntParam(OBJECT_TYPE_COMPONENT_SCRIPT);	
		_generator->addStringParam(component->getScriptPath());
	}

	void GameObjectSerializer::serializeEntityComponent(MeshComponent* component)
	{
		_generator->addIntParam(OBJECT_TYPE_COMPONENT_MESH);	
		_generator->addStringParam(component->getMeshName());
	}

	void GameObjectSerializer::serializeRigidBodyComponent(RigidBodyComponent* component)
	{
		bool isKinematic=component->isKinematic();
		bool useGravity=component->useGravity();
		float mass=component->getMass();
		float restitution=component->getRestitution();
		float friction=component->getFriction();
		Ogre::Vector3 force=component->getConstantForce();

		_generator->addIntParam(OBJECT_TYPE_COMPONENT_RIGIDBODY);
		
		_generator->addBoolParam(isKinematic);
		_generator->addBoolParam(useGravity);
		_generator->addFloatParam(mass);
		_generator->addFloatParam(restitution);
		_generator->addFloatParam(friction);
		
		_generator->addFloatParam(force.x);
		_generator->addFloatParam(force.y);
		_generator->addFloatParam(force.z);
		
	}

	void GameObjectSerializer::serializeBoxColliderComponent(BoxColliderComponent* component)
	{
		Ogre::Vector3 position=component->getPosition();
		Ogre::Vector3 size=component->getSize();

		_generator->addIntParam(OBJECT_TYPE_COMPONENT_BOX_COLLIDER);	
		_generator->addFloatParam(position.x);
		_generator->addFloatParam(position.y);
		_generator->addFloatParam(position.z);

		_generator->addFloatParam(size.x);
		_generator->addFloatParam(size.y);
		_generator->addFloatParam(size.z);
	}

	void GameObjectSerializer::serializeSphereColliderComponent(SphereColliderComponent* component)
	{
		Ogre::Vector3 position=component->getPosition();
		float radius=component->getRadius();

		_generator->addIntParam(OBJECT_TYPE_COMPONENT_SPHERE_COLLIDER);	
		_generator->addFloatParam(position.x);
		_generator->addFloatParam(position.y);
		_generator->addFloatParam(position.z);

		_generator->addFloatParam(radius);
	}

	void GameObjectSerializer::serializePlaneColliderComponent(PlaneColliderComponent* component)
	{
		Ogre::Vector3 normal=component->getNormal();
		float distance=component->getDistance();

		_generator->addIntParam(OBJECT_TYPE_COMPONENT_PLANE_COLLIDER);	
		_generator->addFloatParam(normal.x);
		_generator->addFloatParam(normal.y);
		_generator->addFloatParam(normal.z);

		_generator->addFloatParam(distance);
	}

	void GameObjectSerializer::serializeTransformComponent(TransformComponent* component)
	{
		Ogre::Vector3 pos=component->getPosition();
		Ogre::Quaternion orient=component->getOrientation();
		Ogre::Vector3 scale=component->getScale();

		_generator->addIntParam(OBJECT_TYPE_COMPONENT_TRANSFORM);	
		_generator->addFloatParam(pos.x);
		_generator->addFloatParam(pos.y);
		_generator->addFloatParam(pos.z);
		
		_generator->addFloatParam(orient.w);
		_generator->addFloatParam(orient.x);
		_generator->addFloatParam(orient.y);
		_generator->addFloatParam(orient.z);

		_generator->addFloatParam(scale.x);
		_generator->addFloatParam(scale.y);
		_generator->addFloatParam(scale.z);
	}

	Component* GameObjectSerializer::parseComponent()
	{
		SerializeDataParam* param=_parser->getParam();
		/*
		if(!SerializeDataParam::isComponentType(param)){
			return 0;
		}
		*/

		ObjectType type =(ObjectType)param->getInt();
		switch(type){
			case OBJECT_TYPE_COMPONENT_TRANSFORM:
				return parseTransformComponent();
			case OBJECT_TYPE_COMPONENT_MESH:
				return parseMeshComponent();
			case OBJECT_TYPE_COMPONENT_SCRIPT:
				return parseScriptComponent();
			case OBJECT_TYPE_COMPONENT_RIGIDBODY:
				return parseRigidBodyComponent();
			case OBJECT_TYPE_COMPONENT_PLANE_COLLIDER:
				return parsePlaneColliderComponent();
			case OBJECT_TYPE_COMPONENT_BOX_COLLIDER:
				return parseBoxColliderComponent();
			case OBJECT_TYPE_COMPONENT_SPHERE_COLLIDER:
				return parseSphereColliderComponent();
		}
		return NULL;
	}

	Component* GameObjectSerializer::parseRigidBodyComponent()
	{
		bool isKinematic=false;
		bool useGravity=false;
		float mass=0;
		float restitution=0.1f;
		float friction=0.1f;
		Ogre::Vector3 force(0,0,0);

		SerializeDataParam *param= _parser->getParam();
		if(SerializeDataParam::isBoolParam(param)){
			isKinematic=param->getBool();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isBoolParam(param)){
			useGravity=param->getBool();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			mass=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			restitution=param->getFloat();
		}
		
		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			friction=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			force.x=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			force.y=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			force.z=param->getFloat();
		}

		RigidBodyComponent* component=new RigidBodyComponent();
		component->setKinematic(isKinematic);
		component->setUseGravity(useGravity);
		component->setMass(mass);
		component->setRestitution(restitution);
		component->setFriction(friction);
		component->setConstantForce(force);
		return component;
	}

	Component* GameObjectSerializer::parseSphereColliderComponent()
	{
		Ogre::Vector3 position(0,0,0);
		float radius=1;

		SerializeDataParam *param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			position.x=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			position.y=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			position.z=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			radius=param->getFloat();
		}

		SphereColliderComponent* component=new SphereColliderComponent();
		component->setPosition(position);
		component->setRadius(radius);
		return component;

	}

	Component* GameObjectSerializer::parsePlaneColliderComponent()
	{
		Ogre::Vector3 normal(0,1,0);
		float distance=0;

		SerializeDataParam *param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			normal.x=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			normal.y=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			normal.z=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			distance=param->getFloat();
		}

		PlaneColliderComponent* component=new PlaneColliderComponent();
		component->setNormal(normal);
		component->setDistance(distance);
		return component;
	}

	Component* GameObjectSerializer::parseBoxColliderComponent()
	{
		Ogre::Vector3 position(0,0,0);
		Ogre::Vector3 size(1,1,1);

		SerializeDataParam *param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			position.x=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			position.y=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			position.z=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			size.x=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			size.y=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			size.z=param->getFloat();
		}

		BoxColliderComponent* component=new BoxColliderComponent();
		component->setPosition(position);
		component->setSize(size);
		return component;
	}

	Component* GameObjectSerializer::parseScriptComponent()
	{
		std::string path;
		SerializeDataParam* param=_parser->getParam();
		if(SerializeDataParam::isStringParam(param)){
			path=param->getString();
			ScriptComponent *component=new ScriptComponent(path);
			return component;
		}
		return 0;
	}

	Component* GameObjectSerializer::parseMeshComponent()
	{
		std::string meshName;
		SerializeDataParam* param=_parser->getParam();
		if(SerializeDataParam::isStringParam(param)){
			meshName=param->getString();
			assert(!"Not implemented");
			MeshComponent *component=0;//new EntityComponent(meshName);
			return component;
		}
		return 0;
	}

	Component* GameObjectSerializer::parseTransformComponent()
	{
		float positionX=0;
		float positionY=0;
		float positionZ=0;

		float orientW=1;
		float orientX=0;
		float orientY=0;
		float orientZ=0;

		float scaleX=1;
		float scaleY=1;
		float scaleZ=1;

		SerializeDataParam *param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			positionX=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			positionY=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			positionZ=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			orientW=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			orientX=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			orientY=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			orientZ=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			scaleX=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			scaleY=param->getFloat();
		}

		param= _parser->getParam();
		if(SerializeDataParam::isFloatParam(param)){
			scaleZ=param->getFloat();
		}

		TransformComponent* component=new TransformComponent();
		component->setPosition(positionX,positionY,positionZ);
		component->setOrientation(orientW,orientX,orientY,orientZ);
		component->setScale(scaleX,scaleY,scaleZ);
		return component;

	}


	GameObject* GameObjectSerializer::parseGameObject()
	{
		/*
		if(!SerializeDataParam::isGameObjectType(_parser->getParam())){
			return 0;
		}
		*/
		SerializeDataParam* param=_parser->getParam();
		if(!SerializeDataParam::isStringParam(param)){
			return 0;//game object must have a name
		}

		std::string name=param->getString();
		param=_parser->getParam();
		if(!SerializeDataParam::isIntParam(param)){
			return 0;
		}

		std::vector<Component*> componentList;
		int count=param->getInt();//component count
		for(int i=0;i<count;i++){//parse component
			Component* component=parseComponent();
			if(component){
				componentList.push_back(component);
			}
		}

		GameObject* object= new GameObject(SceneObject::getActive(),GAME_OBJECT_EMPTY);
		object->setName(name);
		int size=componentList.size();
		for(int i=0;i<size;i++){
			componentList[i]->attach(object);
		}

		param=_parser->getParam();
		if(!SerializeDataParam::isIntParam(param)){
			return 0;
		}

		std::vector<GameObject*> childrenList;
		count=param->getInt();
		for(int i=0;i<count;i++){//parse child
			GameObject* child=parseGameObject();
			if(child){
				childrenList.push_back(child);
			}
		}

		size=childrenList.size();
		for(int i=0;i<size;i++){
			object->addChild(childrenList[i]);
		}
		
		return object;
	}

	void GameObjectSerializer::unserializer(SerializeDatagram* dgram)
	{
		_parser=new SerializeParser(dgram);
		SerializeDataId name= _parser->getName();
		if(name!=SerializeDataId_GameObjectTree){
			assert("Bug:GameObjectSerializer::unserializer");
			return;
		}
		parseGameObject();
	}

}
